﻿Imports System.ComponentModel
Imports System.Runtime.InteropServices
Imports System.Drawing
Imports System.Windows.Forms
Imports ajayumi.develop.VBasic.Controls.Forms.Utilities
Imports ajayumi.develop.VBasic.Controls.Forms.Renders
Imports ajayumi.develop.VBasic.Controls.Forms.Events


Namespace Forms
    Public Class FormBase
        Inherits System.Windows.Forms.Form

#Region "Variables"
        Private Shared ReadOnly m_FormRenderChangedEvent As New Object()
        Private m_IsPosChanged As Boolean = False
        Private m_Active As Boolean = True
        Private m_ControlBoxManager As ControlBoxManager = Nothing
#End Region

#Region "Properties"

        Private m_FormRender As FormRenderBase = Nothing
        <Browsable(False)> _
        <DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)> _
        Public Property FormRender() As FormRenderBase
            Get
                If Me.m_FormRender Is Nothing Then
                    Me.m_FormRender = New DefaultRender()
                End If
                Return Me.m_FormRender
            End Get
            Set(ByVal value As FormRenderBase)
                Me.m_FormRender = value
                Me.OnFormRenderChanged(EventArgs.Empty)
            End Set
        End Property

        Private m_BorderWidth As Integer = 3
        <DefaultValue(3), Description("边框宽度"), Category("ajayumi.Properties")> _
        Public Property BorderWidth() As Integer
            Get
                Return m_BorderWidth
            End Get
            Set(ByVal value As Integer)
                If m_BorderWidth <> value Then
                    m_BorderWidth = CInt(IIf(value < 1, 1, value))
                End If
            End Set
        End Property

        Private m_CaptionHeight As Integer = 24
        <DefaultValue(24), Description("标题栏高度"), Category("ajayumi.Properties")> _
        Public Property CaptionHeight() As Integer
            Get
                Return m_CaptionHeight
            End Get
            Set(ByVal value As Integer)
                If m_CaptionHeight <> value Then
                    Me.m_CaptionHeight = CInt(IIf(Me.m_CaptionHeight < Me.m_BorderWidth, Me.m_BorderWidth, value))
                    MyBase.Invalidate()
                End If
            End Set
        End Property

        Private m_CaptionFont As Font = SystemFonts.CaptionFont
        <DefaultValue(GetType(Font), "CaptionFont"), Description("标题栏字体"), Category("ajayumi.Properties")> _
        Public Property CaptionFont() As Font
            Get
                Return m_CaptionFont
            End Get
            Set(ByVal value As Font)
                If value IsNot Nothing Then
                    Me.m_CaptionFont = value
                Else
                    Me.m_CaptionFont = SystemFonts.CaptionFont
                End If
                MyBase.Invalidate(Me.CaptionRect)
            End Set
        End Property

        Private m_MinimizeBoxSize As Size = New Size(32, 18)
        <DefaultValue(GetType(Size), "32, 18"), Description("最小化按钮的大小"), Category("ajayumi.Properties")> _
        Public Property MinimizeBoxSize() As Size
            Get
                Return m_MinimizeBoxSize
            End Get
            Set(ByVal value As Size)
                If Me.m_MinimizeBoxSize <> value Then
                    m_MinimizeBoxSize = value
                    MyBase.Invalidate()
                End If
            End Set
        End Property

        Private m_MaximizeBoxSize As Size = New Size(32, 18)
        <DefaultValue(GetType(Size), "32, 18"), Description("最大化按钮的大小"), Category("ajayumi.Properties")> _
        Public Property MaximizeBoxSize() As Size
            Get
                Return m_MaximizeBoxSize
            End Get
            Set(ByVal value As Size)
                If Me.m_MaximizeBoxSize <> value Then
                    m_MaximizeBoxSize = value
                    MyBase.Invalidate()
                End If
            End Set
        End Property

        Private m_CloseBoxSize As Size = New Size(32, 18)
        <DefaultValue(GetType(Size), "32, 18"), Description("关闭按钮的大小"), Category("ajayumi.Properties")> _
        Public Property CloseBoxSize() As Size
            Get
                Return m_CloseBoxSize
            End Get
            Set(ByVal value As Size)
                If Me.m_CloseBoxSize <> value Then
                    m_CloseBoxSize = value
                    MyBase.Invalidate()
                End If
            End Set
        End Property



        <Browsable(False)> _
        Public ReadOnly Property IconRectangle() As Rectangle
            Get
                If MyBase.ShowIcon AndAlso MyBase.Icon IsNot Nothing Then
                    Dim _Width As Integer = SystemInformation.SmallIconSize.Width
                    Dim _NewWidth As Integer = Me.CaptionHeight - Me.BorderWidth - 4
                    If _NewWidth < _Width Then
                        _NewWidth = _NewWidth
                    End If
                    Return New Rectangle(Me.BorderWidth + Me.BorderWidth, _
                                         Me.BorderWidth + CInt((Me.CaptionHeight - Me.BorderWidth - _NewWidth) / 2), _
                                         _NewWidth, _NewWidth)
                End If
                Return Rectangle.Empty
            End Get
        End Property

        <Browsable(False)> _
        Public ReadOnly Property CaptionRect() As Rectangle
            Get
                Return New Rectangle(0, 0, Me.Width, Me.CaptionHeight)
            End Get
        End Property

        <Browsable(False)> _
        Public ReadOnly Property ControlBoxManager() As ControlBoxManager
            Get
                If Me.m_ControlBoxManager Is Nothing Then
                    Me.m_ControlBoxManager = New ControlBoxManager(Me)
                End If
                Return Me.m_ControlBoxManager
            End Get
        End Property



        Protected Overrides ReadOnly Property DefaultPadding() As System.Windows.Forms.Padding
            Get
                Return New Padding(Me.BorderWidth + Me.FormRender.Factor, _
                                   Me.CaptionHeight, _
                                   Me.BorderWidth + Me.FormRender.Factor, _
                                   Me.BorderWidth + Me.FormRender.Factor)
            End Get
        End Property

        Protected Overrides ReadOnly Property CreateParams() As CreateParams
            Get
                Dim _CreateParams As CreateParams = MyBase.CreateParams

                If Not DesignMode Then
                    _CreateParams.Style = _CreateParams.Style Or CInt(NativeMethods.WindowStyle.WS_THICKFRAME)

                    If ControlBox Then
                        _CreateParams.Style = _CreateParams.Style Or CInt(NativeMethods.WindowStyle.WS_SYSMENU)
                    End If

                    If MinimizeBox Then
                        _CreateParams.Style = _CreateParams.Style Or CInt(NativeMethods.WindowStyle.WS_MINIMIZEBOX)
                    End If

                    If Not MaximizeBox Then
                        _CreateParams.Style = _CreateParams.Style And Not CInt(NativeMethods.WindowStyle.WS_MAXIMIZEBOX)
                    End If

                    If Me.m_IsPosChanged Then
                        _CreateParams.Style = _CreateParams.Style And Not (CInt(NativeMethods.WindowStyle.WS_THICKFRAME) Or CInt(NativeMethods.WindowStyle.WS_SYSMENU))
                        _CreateParams.ExStyle = _CreateParams.ExStyle And Not (CInt(NativeMethods.WindowStyleEx.WS_EX_DLGMODALFRAME) Or CInt(NativeMethods.WindowStyleEx.WS_EX_WINDOWEDGE))
                    End If
                End If

                Return _CreateParams
            End Get
        End Property

#End Region

#Region "Constructor"

        Public Sub New()
            Me.StartPosition = FormStartPosition.CenterScreen
            Me.init()
            Me.setStyles()
        End Sub

        Public Sub New(ByVal render As FormRenderBase)
            Me.New()
            Me.m_FormRender = render
        End Sub

#End Region

#Region "Events"
        <Description("改变绘制窗口的事件"), Category("ajayumi.Events")> _
        Public Custom Event FormRenderChanged As eventhandler
            AddHandler(ByVal value As eventhandler)
                MyBase.Events.AddHandler(m_FormRenderChangedEvent, value)
            End AddHandler

            RemoveHandler(ByVal value As eventhandler)
                MyBase.Events.RemoveHandler(m_FormRenderChangedEvent, value)
            End RemoveHandler

            RaiseEvent(ByVal sender As Object, ByVal e As System.EventArgs)
                Dim _Handler As EventHandler = CType(MyBase.Events(m_FormRenderChangedEvent), EventHandler)
                If _Handler IsNot Nothing Then
                    _Handler(sender, e)
                End If
            End RaiseEvent
        End Event
#End Region

#Region "Methods"
        '[Private]－－－－－－－－－－－－－－－－－－－－－－－－－－－
        Private Sub init()
            MyBase.FormBorderStyle = Windows.Forms.FormBorderStyle.None
            MyBase.Padding = Me.DefaultPadding
            Me.FormRender.InitForm(Me)
        End Sub

        Private Sub setStyles()
            Me.SetStyle(ControlStyles.UserPaint Or _
                     ControlStyles.AllPaintingInWmPaint Or _
                     ControlStyles.OptimizedDoubleBuffer Or _
                     ControlStyles.ResizeRedraw, True)
            Me.UpdateStyles()
        End Sub

        Private Sub setReion()
            If MyBase.Region IsNot Nothing Then
                MyBase.Region.Dispose()
            End If
            MyBase.Region = Me.FormRender.CreateRegion(Me)
        End Sub

        Private Sub wmNcHitTest(ByRef m As Message)
            Dim _LParam As Integer = CInt(m.LParam)
            Dim _Point As New Point(NativeMethods.LOWORD(_LParam), NativeMethods.HIWORD(_LParam))
            _Point = PointToClient(_Point)

            If Me.IconRectangle.Contains(_Point) Then
                m.Result = New IntPtr(CInt(NativeMethods.NCHITTEST.HTSYSMENU))
                Exit Sub
            End If

            Const VAL As Integer = 5

            If _Point.X < VAL AndAlso _Point.Y < VAL Then
                m.Result = New IntPtr(CInt(NativeMethods.NCHITTEST.HTTOPLEFT))
                Exit Sub
            ElseIf _Point.X > Me.Width - VAL AndAlso _Point.Y < VAL Then
                m.Result = New IntPtr(CInt(NativeMethods.NCHITTEST.HTTOPRIGHT))
                Exit Sub
            ElseIf _Point.X < VAL AndAlso _Point.Y > Me.Height - VAL Then
                m.Result = New IntPtr(CInt(NativeMethods.NCHITTEST.HTBOTTOMLEFT))
                Exit Sub
            ElseIf _Point.X > Me.Width - VAL AndAlso _Point.Y > Me.Height - VAL Then
                m.Result = New IntPtr(CInt(NativeMethods.NCHITTEST.HTBOTTOMRIGHT))
                Exit Sub
            ElseIf _Point.Y < VAL Then
                m.Result = New IntPtr(CInt(NativeMethods.NCHITTEST.HTTOP))
                Exit Sub
            ElseIf _Point.Y > Me.Height - VAL Then
                m.Result = New IntPtr(CInt(NativeMethods.NCHITTEST.HTBOTTOM))
                Exit Sub
            ElseIf _Point.X < VAL Then
                m.Result = New IntPtr(CInt(NativeMethods.NCHITTEST.HTLEFT))
                Exit Sub
            ElseIf _Point.X > Me.Width - VAL Then
                m.Result = New IntPtr(CInt(NativeMethods.NCHITTEST.HTRIGHT))
                Exit Sub
            End If

            If _Point.Y < Me.CaptionHeight Then
                If Me.ControlBoxManager.CloseBoxRect.Contains(_Point) = False AndAlso _
                   Me.ControlBoxManager.MinimizeBoxRect.Contains(_Point) = False AndAlso _
                   Me.ControlBoxManager.MaximizeBoxRect.Contains(_Point) = False Then
                    m.Result = New IntPtr(CInt(NativeMethods.NCHITTEST.HTCAPTION))
                    Exit Sub
                End If
            End If

            m.Result = New IntPtr(CInt(NativeMethods.NCHITTEST.HTCLIENT))
        End Sub

        Private Sub WmGetMinMaxInfo(ByRef m As Message)
            Dim minmax As NativeMethods.MINMAXINFO = DirectCast(Marshal.PtrToStructure(m.LParam, GetType(NativeMethods.MINMAXINFO)), NativeMethods.MINMAXINFO)

            If MaximumSize <> Size.Empty Then
                minmax.maxTrackSize = MaximumSize
            Else
                Dim rect As Rectangle = Screen.GetWorkingArea(Me)

                minmax.maxPosition = New Point(rect.X - BorderWidth, rect.Y)
                minmax.maxTrackSize = New Size(rect.Width + BorderWidth * 2, rect.Height + BorderWidth)
            End If

            If MinimumSize <> Size.Empty Then
                minmax.minTrackSize = MinimumSize
            Else
                minmax.minTrackSize = New Size(CloseBoxSize.Width + MinimizeBoxSize.Width + MaximizeBoxSize.Width + SystemInformation.SmallIconSize.Width + BorderWidth * 2 + 3, CaptionHeight)
            End If

            Marshal.StructureToPtr(minmax, m.LParam, False)
        End Sub

        Private Sub wmNcActive(ByRef m As Message)
            If CInt(m.WParam) = 1 Then
                Me.m_Active = True
            Else
                Me.m_Active = False
            End If
            m.Result = NativeMethods.TRUE
            MyBase.Invalidate()
        End Sub


        '[Protected]－－－－－－－－－－－－－－－－－－－－－－－－－－
        Protected Overridable Sub OnFormRenderChanged(ByVal e As EventArgs)
            Me.FormRender.InitForm(Me)
            RaiseEvent FormRenderChanged(Me, e)
            MyBase.Invalidate()
        End Sub


        '[Public]－－－－－－－－－－－－－－－－－－－－－－－－－－－



#End Region

#Region "Override Methods"

        Protected Overrides Sub OnCreateControl()
            MyBase.OnCreateControl()
            Me.setReion()
        End Sub

        Protected Overrides Sub OnSizeChanged(ByVal e As System.EventArgs)
            MyBase.OnSizeChanged(e)
            Me.setReion()
        End Sub

        Protected Overrides Sub OnPaint(ByVal e As System.Windows.Forms.PaintEventArgs)
            Dim _Graphics As Graphics = e.Graphics
            Dim _Rectangle As Rectangle = ClientRectangle
            Dim _FormRender As FormRenderBase = Me.FormRender
            With _FormRender
                .DrawBackground(New BackgroundRenderEventArgs(Me, _Graphics, _Rectangle))
                .DrawCaption(New CaptionRenderEventArgs(Me, Me.m_Active, _Graphics, Me.CaptionRect))
                .DrawBorder(New BorderRenderEventArgs(Me, Me.m_Active, _Graphics, _Rectangle))
            End With

            If Me.ControlBoxManager.CloseBoxVisible Then
                _FormRender.DrawControlBox( _
                    New ControlBoxRenderEventArgs(Me, Me.m_Active, _
                                                  Me.ControlBoxManager.CloseBoxState, _
                                                  ControlBoxStyle.Close, _
                                                  _Graphics, _
                                                  Me.ControlBoxManager.CloseBoxRect))
            End If

            If Me.ControlBoxManager.MaximizeBoxVisibale Then
                _FormRender.DrawControlBox( _
                    New ControlBoxRenderEventArgs(Me, Me.m_Active, _
                                                  Me.ControlBoxManager.MaximizeBoxState, _
                                                  ControlBoxStyle.Maximize, _
                                                  _Graphics, _
                                                  Me.ControlBoxManager.MaximizeBoxRect))
            End If

            If Me.ControlBoxManager.MinimizeBoxVisibale Then
                _FormRender.DrawControlBox( _
                    New ControlBoxRenderEventArgs(Me, Me.m_Active, _
                                                  Me.ControlBoxManager.MinimizeBoxState, _
                                                  ControlBoxStyle.Minimize, _
                                                  _Graphics, _
                                                  Me.ControlBoxManager.MinimizeBoxRect))
            End If
        End Sub

        Protected Overrides Sub WndProc(ByRef m As System.Windows.Forms.Message)
            Select Case m.Msg
                Case CInt(NativeMethods.WindowMessages.WM_NCHITTEST)
                    Me.wmNcHitTest(m)
                    Exit Select
                Case CInt(NativeMethods.WindowMessages.WM_NCPAINT)
                Case CInt(NativeMethods.WindowMessages.WM_NCCALCSIZE)
                    Exit Select
                Case CInt(NativeMethods.WindowMessages.WM_WINDOWPOSCHANGED)
                    Me.m_IsPosChanged = True
                    MyBase.WndProc(m)
                    Me.m_IsPosChanged = False
                    Exit Select
                Case CInt(NativeMethods.WindowMessages.WM_GETMINMAXINFO)
                    Me.WmGetMinMaxInfo(m)
                    Exit Select
                Case CInt(NativeMethods.WindowMessages.WM_NCACTIVATE)
                    Me.wmNcActive(m)
                    Exit Select
                Case Else
                    MyBase.WndProc(m)
                    Exit Select
            End Select
        End Sub

        Protected Overrides Sub OnMouseMove(ByVal e As System.Windows.Forms.MouseEventArgs)
            MyBase.OnMouseMove(e)
            Me.ControlBoxManager.ProcessMouseOperate(e.Location, MouseOperate.Move)
        End Sub

        Protected Overrides Sub OnMouseDown(ByVal e As System.Windows.Forms.MouseEventArgs)
            MyBase.OnMouseDown(e)
            Me.ControlBoxManager.ProcessMouseOperate(e.Location, MouseOperate.Down)
        End Sub

        Protected Overrides Sub OnMouseUp(ByVal e As System.Windows.Forms.MouseEventArgs)
            MyBase.OnMouseUp(e)
            Me.ControlBoxManager.ProcessMouseOperate(e.Location, MouseOperate.Up)
        End Sub

        Protected Overrides Sub OnMouseLeave(ByVal e As System.EventArgs)
            MyBase.OnMouseLeave(e)
            Me.ControlBoxManager.ProcessMouseOperate(Point.Empty, MouseOperate.Leave)
        End Sub

        Protected Overrides Sub OnMouseHover(ByVal e As System.EventArgs)
            MyBase.OnMouseHover(e)
            Me.ControlBoxManager.ProcessMouseOperate(PointToClient(MousePosition), MouseOperate.Hover)
        End Sub


#End Region

    End Class

End Namespace

